/****************************************************************************
 * Copyright (C) 2025 Chenyz. All rights reserved.
 *
 * @file    TinyOS_FlagGroup.c
 *
 * @par     dependencies
 *          - TinyOS_FlagGroup.h (Flag group management definitions)
 *          - TinyOS_Event.h (Event management definitions)
 *          - TinyOS_Task.h (Task management definitions)
 *          - TinyOS_Objects.h (Core data structures)
 *
 * @author  Chenyz
 *
 * @brief   Flag group management module for TinyOS with following features:
 *          - Event flag synchronization with AND/OR conditions
 *          - Flag setting, clearing, and consumption operations
 *          - Task blocking and waking on flag conditions
 *          - Timeout support for flag waiting operations
 *          - Non-blocking flag checking option
 *          - Multiple flag combination support with consumption control
 *          - Critical section protection for thread-safe operations
 *          - Integration with event and task management systems
 *          - Cross-platform compatibility (bare-metal/RTOS systems)
 *
 * @version V1.0
 * @date    2025-05-23
 *
 * @note    Coding standard:
 *          - 4 spaces per indent level
 *          - Follow MISRA-C 2012 guidelines
 *          - Compiler compatibility: C99 and above
 *
 ****************************************************************************/

#include "TinyOS_FlagGroup.h"
#include "TinyOS_Event.h"
#include "TinyOS_Task.h"
#include "TinyOS_Objects.h"


#if TINYOS_ENABLE_FLAGGROUP == 1

/**
 * @brief Checks flag conditions and optionally consumes flags
 * 
 * This internal function checks if the current flag group meets the specified
 * conditions and optionally consumes (clears) the flags if requested.
 * 
 * @param FlagGroup Pointer to the flag group structure
 * @param FlagType Type of flag operation (SET/CLEAR, ALL/ANY, CONSUME)
 * @param FlagMask Pointer to the flag mask to check (updated with result)
 * @return uint32_t Error code indicating if conditions were met
 */
static uint32_t TinyOS_FlagGroupCheckAndConsume(
    TinyOS_FlagGroupType* FlagGroup,
    uint32_t FlagType,
    uint32_t* FlagMask
)
{
    uint32_t SrcFlagMask = *FlagMask;
    uint32_t IsSetFlag = FlagType & TINYOS_FLAGGROUP_SET;
    uint32_t IsAllFlag = FlagType & TINYOS_FLAGGROUP_ALL;
    uint32_t IsConsumeFlag = FlagType & TINYOS_FLAGGROUP_CONSUME;
    
    uint32_t CompareMask;

    /* Check SET or CLEAR condition */
    if(IsSetFlag)
    {
        /* Check if specified flags are SET */
        CompareMask = SrcFlagMask & FlagGroup->FlagGroupMask; 
        
        /* Verify ALL or ANY condition */
        if((IsAllFlag && CompareMask == SrcFlagMask) || (!IsAllFlag && CompareMask != 0))
        {
            /* Conditions met - consume flags if requested */
            if(IsConsumeFlag)
            {
                FlagGroup->FlagGroupMask &= ~CompareMask;
            }
            *FlagMask = CompareMask;
            return TinyOS_ErrorType_NoError;
        }
        else
        {
            /* Conditions not met */
            *FlagMask = CompareMask;
            return TinyOS_ErrorType_SourceUnavailable;
        }             
    }
    else
    {
        /* Check if specified flags are CLEAR */
        CompareMask = SrcFlagMask & ~FlagGroup->FlagGroupMask; 
        
        /* Verify ALL or ANY condition */
        if((IsAllFlag && CompareMask == SrcFlagMask) || (!IsAllFlag && CompareMask != 0))
        {
            /* Conditions met - set flags if consumption requested */
            if(IsConsumeFlag)
            {
                FlagGroup->FlagGroupMask |= CompareMask;
            }
            *FlagMask = CompareMask;
            return TinyOS_ErrorType_NoError;
        }
        else
        {
            /* Conditions not met */
            *FlagMask = CompareMask;
            return TinyOS_ErrorType_SourceUnavailable;
        }           
    }           
}

/**
 * @brief Initializes a flag group with specified initial flags
 * 
 * This function sets up a flag group control block with the specified initial
 * flag values. The flag group is integrated with the event system for task synchronization.
 * 
 * @param FlagGroup Pointer to the flag group structure to be initialized
 * @param InitFlags Initial value of the flag group mask
 */
void TinyOS_FlagGroupInit(
    TinyOS_FlagGroupType* FlagGroup,
    uint32_t InitFlags
)
{
    TinyOS_EventBlockInit(&(FlagGroup->EventBlock), TinyOS_EventTypeFlagGroup);
    FlagGroup->FlagGroupMask = InitFlags;
}

/**
 * @brief Waits for flag conditions to be met with optional timeout
 * 
 * This function checks if the flag group meets the specified conditions. If the
 * conditions are met, it returns immediately. If not, the task will block until
 * the conditions are met or the timeout expires.
 * 
 * @param FlagGroup Pointer to the flag group structure
 * @param FlagType Type of flag operation (SET/CLEAR, ALL/ANY, CONSUME)
 * @param RequestFlagMask Flag mask to wait for
 * @param ResultFlagMask Pointer to store the actual flag mask that matched
 * @param WaitTicks Maximum time to wait for conditions (0 = no waiting)
 * @return uint32_t Error code indicating operation result
 */
uint32_t TinyOS_FlagGroupTake(
    TinyOS_FlagGroupType* FlagGroup, 
    uint32_t FlagType, 
    uint32_t RequestFlagMask, 
    uint32_t* ResultFlagMask, 
    uint32_t WaitTicks
)
{
    uint32_t Stauts = TinyOS_TaskEnterCritical();      
    uint32_t Result;
    uint32_t RequestMask = RequestFlagMask;
    
    /* Check if flag conditions are currently met */
    Result = TinyOS_FlagGroupCheckAndConsume(FlagGroup, FlagType, &RequestMask);
    
    if(Result != TinyOS_ErrorType_NoError)
    {
        /* Conditions not met - prepare task for waiting */
        CurrentTask->TaskWaitFlagGroupType = FlagType;
        CurrentTask->TaskFlagGroupRequestMask = RequestFlagMask;
        
        /* Put task into waiting state */
        TinyOS_EventWait(
            &(FlagGroup->EventBlock), 
            CurrentTask, 
            (void*)0, 
            TinyOS_EventTypeFlagGroup,
            WaitTicks
        );
        TinyOS_TaskExitCritical(Stauts);  
        
        /* Schedule another task while waiting */
        TinyOS_TaskScheduler();
                
        /* Retrieve result after being woken */
        *ResultFlagMask = CurrentTask->TaskFlagGroupRequestMask;
        Result = CurrentTask->TaskEventWaitResult;          
    }
    else
    {
        /* Conditions met - return immediately */
        *ResultFlagMask = RequestMask;
        TinyOS_TaskExitCritical(Stauts);  
    }
    return Result;  
}

/**
 * @brief Checks flag conditions without blocking
 * 
 * This function checks if the flag group meets the specified conditions without
 * blocking the calling task. The function returns immediately with the check result.
 * 
 * @param FlagGroup Pointer to the flag group structure
 * @param FlagType Type of flag operation (SET/CLEAR, ALL/ANY, CONSUME)
 * @param RequestFlagMask Flag mask to check for
 * @param ResultFlagMask Pointer to store the actual flag mask that matched
 * @return uint32_t Error code indicating operation result
 */
uint32_t TinyOS_FlagGroupNoWaitTake(
    TinyOS_FlagGroupType* FlagGroup, 
    uint32_t FlagType, 
    uint32_t RequestFlagMask, 
    uint32_t* ResultFlagMask
)
{
    uint32_t Stauts = TinyOS_TaskEnterCritical();  
    
    uint32_t RequestMask = RequestFlagMask;
    TinyOS_FlagGroupCheckAndConsume(FlagGroup, FlagType, &RequestMask);  
    
    *ResultFlagMask = RequestMask;
    TinyOS_TaskExitCritical(Stauts);  
 
    return TinyOS_ErrorType_NoError;  
}

/**
 * @brief Sets or clears flags in the flag group
 * 
 * This function modifies the flag group by setting or clearing specified flags.
 * After modifying the flags, it checks all waiting tasks to see if their
 * conditions are now met, waking up any tasks whose conditions are satisfied.
 * 
 * @param FlagGroup Pointer to the flag group structure
 * @param SetOrClear Operation type: 1 = set flags, 0 = clear flags
 * @param FlagMask Flag mask indicating which flags to set or clear
 */
void TinyOS_FlagGroupGive(
    TinyOS_FlagGroupType* FlagGroup, 
    uint8_t SetOrClear,
    uint32_t FlagMask
)
{
    uint32_t Stauts = TinyOS_TaskEnterCritical();  
    uint8_t TaskSched = 0;
    TinyOS_ListType* TempFlagGroupWaitList;
    TinyOS_NodeType* TempNode;
    TinyOS_NodeType* NextNode;
    
    /* Set or clear the specified flags */
    if(SetOrClear)
    {
        FlagGroup->FlagGroupMask |= FlagMask;
    }
    else
    {
        FlagGroup->FlagGroupMask &= ~FlagMask;
    }
    
    /* Check all waiting tasks to see if their conditions are now met */
    TempFlagGroupWaitList = &FlagGroup->EventBlock.WaitList;
    for(
        TempNode = TempFlagGroupWaitList->HeadNode.NextNode;
        TempNode != &(TempFlagGroupWaitList->HeadNode);
        TempNode = NextNode  
    )
    {
        /* Get task control block from wait list node */
        TinyOS_TaskType* TempTaskHandler = \
        (TinyOS_TaskType*)TINYOS_NODE_PARENT(TempNode, TinyOS_TaskType, TaskListNode);
        
        uint32_t TempTaskFlagMask = TempTaskHandler->TaskFlagGroupRequestMask;
        uint32_t Result = TinyOS_FlagGroupCheckAndConsume(
            FlagGroup, 
            TempTaskHandler->TaskWaitFlagGroupType, 
            &TempTaskFlagMask
        );
        
        /* Store next node before potentially removing current node */
        NextNode = TempNode->NextNode;
        
        if(Result == TinyOS_ErrorType_NoError)
        {
            /* Conditions met - wake up the task */
            TempTaskHandler->TaskFlagGroupRequestMask = TempTaskFlagMask;
            TinyOS_EventWakeTask(
                &FlagGroup->EventBlock, 
                TempTaskHandler, 
                (void *)0, 
                TinyOS_ErrorType_NoError
            );
            TaskSched = 1;            
        }
    }
    
    /* Schedule tasks if any were woken */
    if(TaskSched)
    {
        TinyOS_TaskScheduler();
    }
    
    TinyOS_TaskExitCritical(Stauts);    
}

/**
 * @brief Retrieves information about a flag group
 * 
 * This function returns the current state of the flag group including its
 * current flag mask and number of tasks waiting for flag conditions.
 * 
 * @param FlagGroup Pointer to the flag group structure
 * @param Info Pointer to a structure where the flag group information will be stored
 */
void TinyOS_FlagGroupGetInfo(
    TinyOS_FlagGroupType* FlagGroup,
    TinyOS_FlagGroupInfo* Info
)
{
    uint32_t Stauts = TinyOS_TaskEnterCritical();    

    Info->FlagGroupMask = FlagGroup->FlagGroupMask;
    Info->TaskCount = TinyOS_EventWaitCount(&(FlagGroup->EventBlock));
    TinyOS_TaskExitCritical(Stauts);
}

/**
 * @brief Deletes the flag group and notifies all waiting tasks
 * 
 * This function removes the flag group from the system, waking up all tasks
 * that were waiting for flag conditions with a deletion notification.
 * 
 * @param FlagGroup Pointer to the flag group structure to be deleted
 * @return uint32_t Number of tasks that were waiting on the flag group
 */
uint32_t TinyOS_FlagGroupDelete(TinyOS_FlagGroupType* FlagGroup)
{
    uint32_t Stauts = TinyOS_TaskEnterCritical();

    /* Wake all tasks waiting on this flag group with deletion notification */
    uint32_t DeleteCount = TinyOS_EventRemoveAll(
        &(FlagGroup->EventBlock), 
        (void*)0, 
        TINYOS_TASK_DELETED
    );
    
    TinyOS_TaskExitCritical(Stauts);
        
    /* Schedule tasks if any were woken */
    if(DeleteCount > 0)
    {
        TinyOS_TaskScheduler();
    }
    
    return DeleteCount;
}

#endif
